


#ifndef __QE_BUFFER_H__
#define __QE_BUFFER_H__



#include "qe_def.h"
#include "qe_list.h"



/** 
 * GenericBufffer(gbuf)
 */
typedef struct 
{
	char         *p;
	unsigned int  size;         /* memory size */
	unsigned int  nbytes;	    /* data numbers in bytes */
	unsigned int  dynamic:1;
	unsigned int  reserve_flags:31;
	qe_list       list;
} qe_gbuf;

#define qe_gbuf_index(buf, i)				((buf)->p[(i)])
#define qe_gbuf_pos(buf)					((buf)->p)
#define qe_gbuf_offs_pos(buf, offs)			((buf)->p + (offs))
#define qe_gbuf_free_pos(buf)				((buf)->p + (buf)->nbytes)
#define qe_gbuf_free_size_remove(buf, n)	((buf)->nbytes += (n))
#define qe_gbuf_data_size_append(buf, n)	((buf)->nbytes += (n))
#define qe_gbuf_data_size(buf)				((buf)->nbytes)
#define qe_gbuf_free_size(buf)				((buf)->size - (buf)->nbytes)
#define qe_gbuf_size(buf)					((buf)->size)

void qe_gbuf_init(qe_gbuf *buf, void *buffer, qe_size size);
void qe_gbuf_clear(qe_gbuf *buf);
void qe_gbuf_backoff(qe_gbuf *buf, int back);
qe_gbuf *qe_gbuf_new(qe_size size);
qe_ret qe_gbuf_append(qe_gbuf *buf, void *data, qe_size length);
qe_ret qe_gbuf_memcpy(qe_gbuf *buf, qe_ptr data, qe_size size);

typedef struct
{
	unsigned int size; 	    /* memory size */
	unsigned int nbytes;	/* data numbers in bytes */
	unsigned int num_bufs;
	qe_list      bufs;
} qe_gbuf_pool;

void qe_gbuf_pool_init(qe_gbuf_pool *pool);
qe_gbuf_pool *qe_gbuf_pool_new(void);
void qe_gbuf_pool_clear(qe_gbuf_pool *pool);
void qe_gbuf_pool_destroy(qe_gbuf_pool *pool);
qe_ret qe_gbuf_pool_append(qe_gbuf_pool *pool, qe_gbuf *buf);
qe_gbuf *qe_gbuf_pool_merge(qe_gbuf_pool *pool);



/**
 * Generic Buffer Ring
 */
typedef struct
{
	qe_uint num_bufs;
	qe_uint head;
	qe_uint tail;
	qe_uint count;
	qe_gbuf *buf;
	qe_gbuf *bds;
	qe_lock lock;
} qe_gbuf_ring;

#define hy_gbuf_ring_size(r)		((r)->num_bufs)
#define qe_gbuf_ring_wait(r)		((r)->count)
#define qe_gbuf_ring_free(r)		((r)->num_bufs - (r)->count)

qe_ret qe_gbuf_ring_init(qe_gbuf_ring *ring, qe_size size, qe_uint num);
qe_gbuf_ring *qe_gbuf_ring_new(qe_size size, qe_uint num);

qe_ret qe_gbuf_ring_push(qe_gbuf_ring *ring, qe_ptr data, qe_size size);
qe_ret qe_gbuf_ring_pop(qe_gbuf_ring *ring, qe_gbuf *desc);

qe_ret qe_gbuf_ring_peek_tail(qe_gbuf_ring *ring, qe_gbuf *desc);
qe_ret qe_gbuf_ring_peek_head(qe_gbuf_ring *ring, qe_gbuf *desc);
qe_ret qe_gbuf_ring_peek_index(qe_gbuf_ring *ring, qe_uint index, 
	qe_gbuf *desc);

qe_ret qe_gbuf_ring_set_lock(qe_gbuf_ring *ring, qe_ptr lock,
	qe_lock_acquire acquire, qe_lock_release release);
qe_ret qe_gbuf_ring_push_locked(qe_gbuf_ring *ring, qe_ptr data, 
	qe_size size, qe_uint wait);
qe_ret qe_gbuf_ring_pop_locked(qe_gbuf_ring *ring, qe_gbuf *desc, 
	qe_uint wait);

qe_bool qe_gbuf_ring_isfull(qe_gbuf_ring *ring);
qe_bool qe_gbuf_ring_isempty(qe_gbuf_ring *ring);

void qe_gbuf_ring_clear(qe_gbuf_ring *ring);
void qe_gbuf_ring_deinit(qe_gbuf_ring *ring);
void qe_gbuf_ring_destroy(qe_gbuf_ring *ring);



/**
 * RingBuffer
 */
typedef struct {
	char *buf;
	qe_uint head;
	qe_uint tail;
	qe_uint count;
	qe_uint size;
	qe_destroy_notify destroy_func;
}qe_ringbuffer;

typedef struct
{
	qe_ringbuffer *rb;
	qe_int pos;
}qe_ringbuffer_iter;

#define qe_ringbuffer_index(r,i)      (((char*) (r)->buf) [((r)->head + (i % (r)->size))])

/**
 * @brief Create a RingBuffer with size
 * 
 * @param size : the memsize to create
 * @return RingBuffer
 * @note use free to release RingBuffer's memory
 */
qe_ringbuffer *qe_ringbuffer_new(qe_size size);

/**
 * @brief Initialize a RingBuffer with external buffer and destroy function
 * 
 * @param[in] rb: RingBuffer
 * @param[in] buf: external buffer pointer
 * @param[in] size: external buffer size
 * @param[in] destroy_func: external buffer destroy function
 * 
 * @note This function is the fully version of initializing the RingBuffer,
 * qe_ringbuffer_init() and qe_ringbuffer_new() will auto call this function.
 * RingBuffer support 3 create case as follow:
 *     1. call qe_ringbuffer_new() to dynamic create with a size. 
 *     2. call qe_ringbuffer_init() to init with a external static buffer.
 *     3. call qe_ringbuffer_init_full() to init with a external static/dynamic
 *        buffer and destroy function.
 * If in 'case 2', call qe_ringbuffer_destroy() will only clear data, but not
 * free buffer, if buffer is dynamic, you must manually release it. If in 
 * 'case 3', and qe_ringbuffer_init_full() give a destroy function, call 
 * qe_ringbuffer_destroy() will auto invoke destroy function. If in 'case 3' but
 * qe_ringbuffer_init_full() don't give destroy function, call 
 * qe_ringbuffer_destroy() only clear data.
 */
qe_ret qe_ringbuffer_init_full(qe_ringbuffer *rb, qe_ptr buf, 
	qe_size size, qe_destroy_notify destroy_func);

/**
 * @brief Initialize RingBuffer with external static buffer
 * 
 * @param[in] rb : RingBuffer
 * @param[in] buf : external buffer pointer
 * @param size : external buffer size
 */
qe_ret qe_ringbuffer_init(qe_ringbuffer *rb, qe_ptr buf, qe_size length);

/**
 * @brief Clear a RingBuffer, this function will not modify the data
 * 
 * @param rb : RingBuffer pointer
 */
void qe_ringbuffer_clear(qe_ringbuffer *rb);

/**
 * @brief Write data to RingBuffer
 * 
 * @param[in] rb  : RingBuffer pointer
 * @param[in] buf : write buffer data
 * @param[in] len : write buffer length
 * 
 * @return RingBuffer wait length
 */
qe_uint qe_ringbuffer_write(qe_ringbuffer *rb, qe_ptr buf, qe_uint len);

/**
 * @brief Read data from RingBuffer
 * 
 * @param rb  : RingBuffer pointer
 * @param buf : read buffer pointer
 * @param len : read buffer length
 * 
 * @return read size
 */
qe_uint qe_ringbuffer_read(qe_ringbuffer *rb, char *buf, qe_uint len);

/**
 * @brief Peek data from RingBuffer, will not move head pos
 * 
 * @param rb  : RingBuffer pointer
 * @param buf : read buffer pointer
 * @param len : read buffer length
 * 
 * @return read size
 */
qe_uint qe_ringbuffer_peek(qe_ringbuffer *rb, char *buf, qe_uint len);

/**
 * @brief Remove data from RingBuffer
 * 
 * @param rb  : RingBuffer pointer
 * @param buf : read buffer pointer
 * @param len : read buffer length
 * 
 */
void qe_ringbuffer_remove(qe_ringbuffer *rb, qe_uint len);

/**
 * @brief Get the valid data size in RingBuffer
 * 
 * @param rb : RingBuffer pointer
 * @return wait data size
 */
unsigned int qe_ringbuffer_wait(qe_ringbuffer *rb);

/**
 * @brief Get RingBuffer free memory size
 * 
 * @param rb : RingBuffer pointer
 * @return free size
 */
qe_uint qe_ringbuffer_freesize(qe_ringbuffer *rb);

/**
 * @brief Get the RingBuffer's memory capacity
 * 
 * @param rb : RingBuffer pointer
 * @return capacity bytes
 */
qe_uint qe_ringbuffer_capacity(qe_ringbuffer *rb);


/**
 * @brief  Indicate if RingBuffer is full
 * 
 * @return qe_true:full qe_false:not full
 */
qe_bool qe_ringbuffer_isfull(qe_ringbuffer *rb);

/**
 * @brief Indicate if RingBuffer is empty
 * 
 * @return qe_true:empty qe_false:not empty
 */
qe_bool qe_ringbuffer_isempty(qe_ringbuffer *rb);

/**
 * @brief Destory a RingBuffer
 * 
 * @param[in] rb: RingBuffer
 * 
 * @note This function will check RingBuffer's memory create way,
 * if use qe_ringbuffer_int() init RingBuffer with a external buffer,
 * this function will only 
 */
void qe_ringbuffer_destroy(qe_ringbuffer *rb);

qe_ret qe_ringbuffer_iter_init(qe_ringbuffer_iter *iter, qe_ringbuffer *rb);

qe_bool qe_ringbuffer_iter_next(qe_ringbuffer_iter *iter, char *data);



#endif